package org.mcflier.appuntamento.tasks;

import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import org.mcflier.appuntamento.core.Factory;
import org.mcflier.appuntamento.core.exception.ModulesException;
import org.mcflier.appuntamento.core.logger.LoggerManager;
import org.mcflier.appuntamento.core.modules.IModule;
import org.mcflier.appuntamento.core.modules.ModuleNode;

public class TaskManager implements IModule {
	
	private static final String TASKS_TAG = "tasks";
	private static final String TASK_TAG = "task";
	private static final String NAME_TAG = "name";
	private static final String DELAY_TAG = "delay";
	private static final String PATH_TAG = "path";

	private Map<String, AbstractTask> tasks;
	private Logger logger;
	
	public TaskManager() throws ModulesException{
		this.tasks = new HashMap<String, AbstractTask>();
		this.logger = ((LoggerManager)Factory.getInstance().getModuleInstance(Factory.MODULE_LOGGER)).getLogger(this.getClass().getName());
	}
	
	@Override
	public void receiveModuleContent(ModuleNode node) {
		node = node.getModule(TASKS_TAG);
		if(node.getModules().size()==0 && node.getAttributes().size()!=0){
			Set<String> keys = node.getAttributes().keySet();
			ModuleNode temp = new ModuleNode(TASK_TAG);
			for (String key : keys) {
				temp.add(key, node.getAttribute(key));
			}
			node.getAttributes().clear();
			node.add(temp.getName(), temp);
		}
		
		Set<String> keys = node.getModules().keySet();
		AbstractTask task;
		String name, path;
		long delay = 6000;
		ModuleNode temp;
		for (String key : keys) {
			temp = node.getModule(key);
			name = temp.getAttribute(NAME_TAG);
			if(name==null){
				this.logger.warning("Error retrieving a without name. Name is completely necessary");
				break;
			}
			
			try{
				delay = Long.parseLong(temp.getAttribute(DELAY_TAG));
				if(delay<60000){
					delay = 60000;
					this.logger.warning("Task delay '"+name+"' increased to 60000 (minimum delay)");
				}
			}
			catch(NumberFormatException e){
				this.logger.warning("Error retieving the task '"+name+"'. It has an invalid delay (it must be an integer number greater than zero, expressed in millis)");
				break;
			}
			
			path = temp.getAttribute(PATH_TAG);
			if(path == null){
				this.logger.warning("Error retrievin the task '"+name+"'. It has not a task path");
				break;
			}
			
			try {
				task = (AbstractTask)Class.forName(path).getConstructors()[0].newInstance(name, delay);
				this.tasks.put(task.getName(), task);
			} catch (IllegalArgumentException e) {
				this.logger.warning("Error creating the task '"+name+"'. The constructor receives different parameters");
				break;
			} catch (SecurityException e) {
				this.logger.warning("Error creating the task '"+name+"'.\n"+e.getMessage());
				break;
			} catch (InstantiationException e) {
				this.logger.warning("Error creating the task '"+name+"'. This class must be instantiable (not abstract, interface ...)");
				break;
			} catch (IllegalAccessException e) {
				this.logger.warning("Error creating the task '"+name+"'. This class needs a public constructor and it must be an instantiable class");
				break;
			} catch (InvocationTargetException e) {
				this.logger.warning("Error creating the task '"+name+"'.\n"+e.getMessage());
				break;
			} catch (ClassNotFoundException e) {
				this.logger.warning("Error creating the task '"+name+"'. The class '"+path+"' is missing");
				break;
			}
		}
	}
	
	public AbstractTask getTask(String name){
		return this.tasks.get(name);
	}
	
	public void beginTasks(){
		Collection<AbstractTask> tasks = this.tasks.values();
		for (AbstractTask task : tasks) {
			this.beginTask(task);
		}
	}
	
	public void beginTask(AbstractTask task){
		this.logger.info("Launching task: "+task.getName());
		task.start();
	}
	
	public void stopTasks(){
		Collection<AbstractTask> tasks = this.tasks.values();
		for (AbstractTask task : tasks) {
			this.stopTask(task);
		}
	}
	
	public void stopTask(AbstractTask task){
		this.logger.info("Stopping task: "+task.getName());
		task.stopTask();
	}
}